package checklist.db;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import checklist.entity.User;

public class MyFileEntityManager implements MyEntityManager {
	private static final String FILEPATH = "./app.xml";
	private Document doc = null;

	private Document getDocument() {
		if (doc == null) {
			try {
				if (new File(FILEPATH).exists()) {
					doc = DocumentBuilderFactory.newInstance()
							.newDocumentBuilder().parse(FILEPATH);

				} else {
					doc = DocumentBuilderFactory.newInstance()
							.newDocumentBuilder().newDocument();
					doc.appendChild(doc.createElement("app"));
				}
			} catch (Exception e) {
				throw new RuntimeException(e.getMessage());
			}
		}

		return doc;
	}

	private void save() {
		try {
			TransformerFactory
					.newInstance()
					.newTransformer()
					.transform(new DOMSource(getDocument()),
							new StreamResult(new File(FILEPATH)));
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	private List<String[]> generateCriteria(String query) {
		List<String[]> criteriaList = new ArrayList<String[]>();

		for (String pair : query.split(";")) {
			String[] criteria = pair.split("=");
			if (criteria.length > 1) {
				criteriaList.add(criteria);
			}
		}

		return criteriaList;
	}

	private boolean findAll(List<String[]> criteriaList, NamedNodeMap nodeMap) {
		boolean returnValue = false;

		for (String[] criteria : criteriaList) {
			if (criteria.length > 1) {
				Node node = nodeMap.getNamedItem(criteria[0]);
				if (node != null && node.getNodeValue().equals(criteria[1])) {
					returnValue = true;
				} else {
					return false;
				}
			}
		}
		return returnValue;
	}

	private String searchTagCriteria(List<String[]> criteriaList) {
		for (String[] criteria : criteriaList) {
			if (criteria[0].equals("tag")) {
				criteriaList.remove(criteria);
				return criteria[1];
			}
		}
		return "";
	}

	public Object query(String query) {
		List<String[]> criteriaList = generateCriteria(query);

		NodeList nodeList = getDocument().getElementsByTagName(
				searchTagCriteria(criteriaList));
		for (int i = 0; i < nodeList.getLength(); i++) {
			if (findAll(criteriaList, nodeList.item(i).getAttributes())) {
				return nodeList.item(i);
			}
		}
		return null;
	}

	public void persist(Object object) {
		try {
			String id = object.getClass()
					.getDeclaredMethod("getId", new Class[0])
					.invoke(object, new Object[0]).toString();

			Node node = (Node) query(String.format("tag=%s;id=%s", object
					.getClass().getName(), id));
			if (node != null) {
				node.getParentNode().removeChild(node);
			}

			Element element = getDocument().createElement(
					object.getClass().getName());
			for (PropertyDescriptor property : Introspector.getBeanInfo(
					object.getClass()).getPropertyDescriptors()) {
				element.setAttribute(property.getDisplayName(), property
						.getReadMethod().invoke(object, new Object[0])
						.toString());
			}

			String parentTagName = String.format("list-%s", object.getClass()
					.getName());
			NodeList list = getDocument().getElementsByTagName(parentTagName);
			(list != null && list.getLength() > 0 ? list.item(0)
					: getDocument().getDocumentElement().appendChild(
							getDocument().createElement(parentTagName)))
					.appendChild(element);

			save();
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}
}
